package com.bhajanswarupa.paginationutils;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.ScrollableResults;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HibernatePage<T> extends PageImpl<T> implements Page<T> {

	private static final long serialVersionUID = 3325924460696077762L;

	private ScrollableResults scrollableResults;

    private static final Logger LOGGER = LoggerFactory.getLogger(HibernatePage.class);

	/**
	 * Construct a new Page. Page numbers are zero-based, so the first page is
	 * page 0.
	 * 
	 * @param query
	 *            the Hibernate Query
	 * @param pageNumber
	 *            the page number (zero-based)
	 * @param pageSize
	 *            the number of results to display on the page
	 */
	@SuppressWarnings("unchecked")
	public HibernatePage(Query query, int pageNumber, int pageSize) {

		this.pageNumber = pageNumber;
		this.pageSize = pageSize;
		try {
			scrollableResults = query.scroll();
			/*
			 * We set the max results to one more than the specified pageSize to
			 * determine if any more results exist (i.e. if there is a next page
			 * to display). The result set is trimmed down to just the pageSize
			 * before being displayed later (in getList()).
			 */
			results = query.setFirstResult(pageNumber * pageSize)
					.setMaxResults(pageSize + 1).setCacheable(true).list();

			// set totalNumberOfElements
			initialiseTotalNumberOfElements();

			if (pageNumber > getLastPageNumber()) {
				this.pageNumber = getLastPageNumber();
				results = query.setFirstResult(getLastPageNumber() * pageSize)
						.setMaxResults(pageSize + 1).setCacheable(true).list();
				// set totalNumberOfElements
				initialiseTotalNumberOfElements();
			}

		} catch (HibernateException e) {
			LOGGER.error("Failed to get paginated results: " + e);
		}
	}

	/**
	 * Construct a new Page. Page numbers are zero-based, so the first page is
	 * page 0.
	 * 
	 * @param query
	 *            the Hibernate Query
	 */
	@SuppressWarnings("unchecked")
	public HibernatePage(Query query) {

		pageNumber = 0;
		pageSize = PAGESIZE_ALLRESULTS;

		scrollableResults = query.scroll();
		results = query.setCacheable(true).list();

		// set totalNumberOfElements
		initialiseTotalNumberOfElements();
	}

	/**
	 * Construct a new Page. Page numbers are zero-based, so the first page is
	 * page 0.
	 * 
	 * @param criteria
	 *            the Hibernate Criteria
	 * @param pageNumber
	 *            the page number (zero-based)
	 * @param pageSize
	 *            the number of results to display on the page
	 */
	@SuppressWarnings("unchecked")
	public HibernatePage(Criteria criteria, int pageNumber, int pageSize) {

		this.pageNumber = pageNumber;
		this.pageSize = pageSize;
		try {
			scrollableResults = criteria.scroll();
			/*
			 * We set the max results to one more than the specified pageSize to
			 * determine if any more results exist (i.e. if there is a next page
			 * to display). The result set is trimmed down to just the pageSize
			 * before being displayed later (in getList()).
			 */
			results = criteria.setFirstResult(pageNumber * pageSize)
					.setMaxResults(pageSize + 1).setCacheable(true).list();

			// set totalNumberOfElements
			initialiseTotalNumberOfElements();

			if (pageNumber > getLastPageNumber()) {
				this.pageNumber = getLastPageNumber();
				results = criteria
						.setFirstResult(getLastPageNumber() * pageSize)
						.setMaxResults(pageSize + 1).setCacheable(true).list();

				// set totalNumberOfElements
				initialiseTotalNumberOfElements();
			}

		} catch (HibernateException e) {
			LOGGER.error("Failed to get paginated results", e);
		}

	}

	/**
	 * Construct a new Page. Page numbers are zero-based, so the first page is
	 * page 0.
	 * 
	 * @param criteria
	 *            the Hibernate Criteria
	 */
	@SuppressWarnings("unchecked")
	public HibernatePage(Criteria criteria) {

		pageNumber = 0;
		pageSize = PAGESIZE_ALLRESULTS;

		scrollableResults = criteria.scroll();
		results = criteria.setCacheable(true).list();

		// set totalNumberOfElements
		initialiseTotalNumberOfElements();
	}

	/**
	 * 
	 */
	private void initialiseTotalNumberOfElements() {

		try {
			getScrollableResults().last();
			totalResults = getScrollableResults().getRowNumber() + 1;
		} catch (HibernateException e) {
			LOGGER.error("Failed to get last row number from scollable results", e);
		}
	}

	/**
	 * @return
	 */
	protected ScrollableResults getScrollableResults() {

		return scrollableResults;
	}

}
